home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_300
/
319_01
/
dir.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-06-16
|
21KB
|
1,023 lines
/*
CPP V5 -- directives
source: dir.c
started: October 7, 1985
version: May 31, 1988
External routines defined in this file: do_pp
Written by Edward K. Ream.
This software is in the public domain.
See the read.me file for disclaimer and other information.
*/
#include "cpp.h"
#define TRACE_LINE(name) TRACEP(name, printf("line %d\n", t_line))
static long eval (void);
static long eval1 (void);
static bool gt_prec (en_tokens, en_tokens);
static bool isfnch (int, en_tokens);
static void pp_elif (void);
static void pp_else (void);
static void pp_endif (void);
static void pp_enum (void);
static void pp_error (void);
static void pp_if (void);
static void pp_ifdef (bool);
static void pp_incl (void);
static void pp_line (void);
static void pp_undef (void);
static int prec (int);
static void push_op (en_tokens);
static en_tokens pop_op (void);
static void push_val (long);
static long pop_val (void);
static void skip_lines (void);
/*
Do one preprocessor directive.
*/
#define EQL(string) str_eq(t_symbol+1,string+1)
void
do_pp()
{
TICK("do_pp");
skip_ws(FALSE);
/* Get the directive into t_symbol[]. */
if (!isalpha(ch)) {
goto not_alpha;
}
t_id(t_symbol, MAX_SYMBOL);
/* 3/3/89: bug fix: full white space allowed here. */
skip_ws(FALSE);
/* Skip simple white space after the directive. */
/* -----
while (ch == ' ' || ch == '\t') {
sysnext();
}
----- */
switch(t_symbol [0]) {
case 'd':
if (t_length == 6 && EQL("define")) {
pp_def();
return;
}
goto not_pp;
case 'e':
if (t_length == 4) {
if (EQL("else")) {
pp_else();
return;
}
else if (EQL("elif")) {
pp_elif();
return;
}
}
#ifdef HAS_PP_ENUM
else if (t_length == 4 && EQL("enum")) {
pp_enum();
return;
}
#endif
else if (t_length == 5 && EQL("endif")) {
pp_endif();
return;
}
else if (t_length == 5 && EQL("error")) {
pp_error();
return;
}
goto not_pp;
case 'i':
switch(t_length) {
case 2: if (EQL("if")) {
pp_if();
return;
}
goto not_pp;
case 5: if (EQL("ifdef")) {
pp_ifdef(TRUE);
return;
}
goto not_pp;
case 6: if (EQL("ifndef")) {
pp_ifdef(FALSE);
return;
}
goto not_pp;
case 7: if (EQL("include")) {
pp_incl();
return;
}
goto not_pp;
}
goto not_pp;
case 'l':
if (t_length == 4 && EQL("line")) {
pp_line();
return;
}
goto not_pp;
case 'p':
if (t_length == 6 && EQL("pragma")) {
/* Do NOTHING!! */
skip_pp();
return;
}
goto not_pp;
case 'u':
if (t_length == 5 && EQL("undef")) {
pp_undef();
return;
}
goto not_pp;
default:
goto not_pp;
}
not_alpha:
/*
Be more permissive than the new C standard.
Just skip the rest of the line.
*/
skip_pp();
return;
not_pp:
err2(t_symbol, " is not a valid preprocessor directive.");
skip_pp();
}
#undef EQL
/*
Handle the #error directive.
Produce a diagnostic message.
*/
static char err_msg[] = "#error: ";
static void
pp_error()
{
char message[MAX_SYMBOL];
int i;
strcpy(message, err_msg);
for (i = strlen(message); i < MAX_SYMBOL; i++) {
if (ch == '\n' || ch == END_FILE) {
break;
}
else {
message[i] = ch;
sysnext();
}
}
message[i] = '\0';
fatal(message);
}
/*
Evaluate a constant expression to either true or false.
A constant expression consists of:
1. integer constants or character constants
2. the unary - + and ~ operators
3. the binary + - * / & | ^ << >> == != < > <= >= oprators
4. the ternary ? : operator
5. the ( and ) groupers.
Identifiers are expanded if they are defined, otherwise they
are taken to have a value of zero. All arithmetic is integer and
ints are expanded to long.
*/
#define MAX_EVAL_VAL 100
#define MAX_EVAL_OP 50
static long val_stack[MAX_EVAL_VAL];
static int val_ptr = 0;
static en_tokens op_stack[MAX_EVAL_OP];
static int op_ptr = 0;
static long result;
static bool paren_seen;
static bool error_seen;
static long
eval()
{
TRACETOK("eval");
error_seen = FALSE;
get_token(TRUE);
result = eval1();
RETURN_LONG("eval", result);
}
static char * junk;
static int junki;
static long
eval1()
{
register en_tokens op, op2;
register long val1, val2, val3;
int op_1ptr;
TRACETOK("eval1");
op_1ptr = op_ptr;
/* State S1: unary +, unary -, !, ~, constant or id is expected here. */
s1:
TRACEPN("v_eval1", printf("at state 1\n"));
while (is(PLUS_TOK) || is(MINUS_TOK) || is(TILDE_TOK) || is(NOT_TOK)) {
if (is(PLUS_TOK)) {
push_op(UPLUS_TOK);
}
else if (is(MINUS_TOK)) {
push_op(UMINUS_TOK);
}
else if (is(NOT_TOK)) {
push_op(NOT_TOK);
}
else {
push_op(TILDE_TOK);
}
get_token(TRUE);
}
/* We expect a constant or identifier here. */
if (is(INT_TOK) || is(LONG_TOK) || is(CHAR_TOK)) {
push_val((long) t_value);
get_token(TRUE);
}
else if (is(ID_TOK)) {
/* Special case defined id and defined(id). */
if (str_eq(t_symbol, "defined")) {
/* Do not macro expand an id here! */
get_token(FALSE);
if (!is(ID_TOK) && !is(LPAREN_TOK)) {
error("Id or '(' expected after 'defined'.");
goto bad_expr;
}
paren_seen = is(LPAREN_TOK);
if (paren_seen) {
get_token(FALSE);
if (!is(ID_TOK)) {
error("Id expected after '('.");
goto bad_expr;
}
}
if(mst_lookup(t_symbol, &junk, &junki)) {
push_val(1L);
}
else {
push_val(0L);
}
get_token(TRUE);
if (paren_seen) {
if (is(RPAREN_TOK)) {
get_token(TRUE);
}
else {
error("')' expected.");
goto bad_expr;
}
}
}
else {
/* The identifier must be undefined, so it gets 0. */
push_val(0L);
get_token(TRUE);
}
}
else if (is(LPAREN_TOK)) {
get_token(TRUE);
/* Evaluate the expression recursively. */
result = eval1();
if (is(RPAREN_TOK)) {
get_token(TRUE);
push_val(result);
}
else {
error("')' expected.");
goto bad_expr;
}
}
else {
error("Integer constant or parenthesized expression expected.");
goto bad_expr;
}
/* Perform all unary ops and enter state S2. */
TRACEPN("v_eval1", printf("at state 1A\n"));
while (op_ptr > op_1ptr) {
switch (op = pop_op()) {
case UPLUS_TOK: break;
case UMINUS_TOK: push_val(-pop_val()); break;
case NOT_TOK: push_val((long)(!pop_val())); break;
case TILDE_TOK: push_val(~pop_val()); break;
default: push_op(op); goto s2;
}
}
/* State S2: binary op or end_of_expression expected here. */
s2:
TRACEPN("v_eval1", printf("at state 2\n"));
/*
Perform binary operators until the operator stack is
empty or until token operator has a higher precedence
than the operator on the top of the operator stack.
*/
while (op_ptr > op_1ptr && gt_prec(op_stack[op_ptr - 1], token)) {
val2 = pop_val();
val1 = pop_val();
op = pop_op();
switch (op) {
case PLUS_TOK: push_val(val1 + val2); break;
case MINUS_TOK: push_val(val1 - val2); break;
case STAR_TOK: push_val(val1 * val2); break;
case DIV_TOK: push_val((long)(val2?(val1/val2):0));
break;
case MOD_TOK: push_val(val1 % val2); break;
case AND_TOK: push_val(val1 & val2); break;
case OR_TOK: push_val(val1 | val2); break;
case XOR_TOK: push_val(val1 ^ val2); break;
case LSHIFT_TOK: push_val(val1 << val2); break;
case RSHIFT_TOK: push_val(val1 >> val2); break;
case EQUAL_TOK: push_val((long)(val1 == val2)); break;
case NE_TOK: push_val((long)(val1 != val2)); break;
case LT_TOK: push_val((long)(val1 < val2)); break;
case GT_TOK: push_val((long)(val1 > val2)); break;
case LE_TOK: push_val((long)(val1 <= val2)); break;
case GE_TOK: push_val((long)(val1 >= val2)); break;
case LAND_TOK: push_val((long)(val1 && val2)); break;
case LOR_TOK: push_val((long)(val1 || val2)); break;
case COLON_TOK: op2 = pop_op()